package me.nibo.webframework.service.account;

import me.nibo.webframework.commons.VariableUtils;
import me.nibo.webframework.commons.enumeration.ResourceType;
import me.nibo.webframework.service.ServiceException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 公用授权抽象类，用于 apache shiro 在执行授权操作时，通过该类去统一对本系统的功能进行授权。
 *
 * @author maurice
 */
public abstract class AuthorizationRealm extends AuthorizingRealm {

    @Autowired
    private AccountService accountService;

    // 默认 premission
    private List<String> defaultPermission = new ArrayList<>();

    /**
     * 设置默认的 permission
     *
     * @param defaultPermissionString permission 如果存在多个值，使用逗号","分割
     */
    public void setDefaultPermissionString(String defaultPermissionString) {
        String[] perms = StringUtils.split(defaultPermissionString, ",");
        CollectionUtils.addAll(defaultPermission, perms);
    }

    /**
     * 设置默认的 permission
     *
     * @param defaultPermission permission 集合
     */
    public void setDefaultPermission(List<String> defaultPermission) {
        this.defaultPermission = defaultPermission;
    }

    /**
     * 当用户进行访问链接时的授权方法
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        NavAuthorizationInfo info = new NavAuthorizationInfo();
        Map<String, Object> user = VariableUtils.cast(principals.fromRealm(getName()).iterator().next());

        if (user == null) {
            throw new ServiceException("当前不存在用户");
        }

        Long id = VariableUtils.cast(user.get("id"), Long.class);
        // 加载用户拥有的资源信息
        List<Map<String, Object>> authorizationInfo = accountService.getUserResources(id);
        // 用用户拥有的资源并和成首页导航
        List<Map<String, Object>> navInfo = accountService.mergeResources(authorizationInfo, ResourceType.SECURITY);
        // 添加用户拥有的 permission
        addPermissions(info, authorizationInfo);
        // 添加用户拥有的首页导航
        info.addNavs(navInfo);

        return info;
    }

    /**
     * 获取授权信息类
     *
     * @param principals shiro 当事人(当前用户)集合
     * @return 授权信息类
     */
    public AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        // 由于 shiro 封装得太死，父类的 getAuthorizationInfo 不是 public 的，
        // 所有要通过该方法去获取授权信息
        return super.getAuthorizationInfo(principals);
    }

    /**
     * 重写父类方法，在获取认证缓存 key 的时候，用登录帐号去做 key
     */
    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        return getUsername(principals);
    }

    /**
     * 重写父类方法，在获取授权缓存 key 的时候，用登录帐号去做 key
     */
    @Override
    protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
        return getUsername(principals);
    }

    /**
     * 通过当前 realm 的 PrincipalCollection 获取用户登录账户
     *
     * @param principals 当前 realm 的 PrincipalCollection
     * @return 登录账户
     */
    @SuppressWarnings("ConstantConditions")
    protected Object getUsername(PrincipalCollection principals) {
        Map<String, Object> user = VariableUtils.cast(principals.fromRealm(getName()).iterator().next());
        return user.get("username");
    }

    /**
     * 通过资源集合，将集合中的 permission 字段内容解析后添加到 SimpleAuthorizationInfo 授权信息中
     *
     * @param info              授权信息
     * @param authorizationInfo 资源集合
     */
    private void addPermissions(SimpleAuthorizationInfo info, List<Map<String, Object>> authorizationInfo) {

        List<String> permissions = new ArrayList<>();

        //添加默认的permissions到permissions
        if (CollectionUtils.isNotEmpty(defaultPermission)) {
            CollectionUtils.addAll(permissions, defaultPermission.iterator());
        }

        //解析当前用户资源中的permissions
        for (Map<String, Object> m : authorizationInfo) {
            Object permission = m.get("permission");

            if (permission == null || StringUtils.isEmpty(permission.toString())) {
                continue;
            }

            permissions.add(StringUtils.substringBetween(permission.toString(), "perms[", "]"));
        }

        //将当前用户拥有的permissions设置到SimpleAuthorizationInfo中
        info.addStringPermissions(permissions);

    }
}
